﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Buddy.BehaviorTree;
using Buddy.Common;
using Buddy.Common.Math;
using Buddy.CommonBot;
using Buddy.Swtor;
using Buddy.Swtor.Objects;


namespace WingIt.Routines.Mirrors
{
    [RevisionControlId("$Id: MirrorExtensions.cs 686 2012-08-12 16:12:20Z Chinajade $")]
    public static class MirrorExtensions
    {
        public delegate bool    TorEffectPredicateDelegate(TorEffect torEffect);

        /// <summary>
        /// <para>Returns true, if TorCharacter can cast ABILITYNAME on ONTARGET.</para>
        /// <para>Notes:<list type="bullet">
        /// <item><description><para> * Due to BW API limitations, TorCharacter _must_ be <c>BuddyTor.Me</c>.
        /// Attempts to call this method with any other TorCharacter will cause a contract violation.</para></description>"</item>
        /// </list></para>
        /// </summary>
        /// <param name="torCharacter"></param>
        /// <param name="abilityName"></param>
        /// <param name="onTarget"></param>
        /// <returns></returns>
        public static bool      CanMirrorCast(this TorCharacter torCharacter,
                                              string            abilityName,
                                              TorCharacter      onTarget)
        {
            // Extension methods guarantee the 'this' argument is never null, so no need to check a contract here
            Logger.ContractRequires(() => torCharacter == BuddyTor.Me, () => "torCharacter == BuddyTor.Me");
            Logger.ContractRequires(() => !string.IsNullOrEmpty(abilityName), () => "!string.IsNullOrEmpty(abilityName)");
            Logger.ContractRequires(() => onTarget != null, () => "onTarget != null");

            string mirrorAbilityName = Mirror.LookupAbilityName(abilityName);
            return (AbilityManager.CanCast(mirrorAbilityName, onTarget));
        }

        /// <summary>
        /// <para>Returns the appropriate ability, if the TorCharacter's class or its mirror-class has an ability called ABILITYNAME.</para>
        /// <para>Otherwise, this method returns null.</para>
        /// </summary>
        /// <param name="torCharacter"></param>
        /// <param name="abilityName"></param>
        /// <returns></returns>
        public static TorAbility    GetMirrorAbility(this TorCharacter  torCharacter,
                                                     string             abilityName)
        {
            // Extension methods guarantee the 'this' argument is never null, so no need to check a contract here
            Logger.ContractRequires(() => !string.IsNullOrEmpty(abilityName), () => "!string.IsNullOrEmpty(abilityName)");

            string mirrorAbilityName = Mirror.LookupAbilityName(abilityName);
            return (torCharacter.KnownAbilitiesContainer.FirstOrDefault(a => (a.Name == mirrorAbilityName)));
        }

        /// <summary>
        /// <para>Returns true, if the TorCharacter has BUFFNAME via boolean implication.</para>
        /// <para>In other words, 'true' is returned if the BUFFNAME ability is _not_ known, or the BUFFNAME ability _is_ known _and_ the BUFFNAME buff is present.</para>
        /// <para>This pattern occurs frequently when writing Routines.
        /// "Implication" is a boolean operator (like And, Or, Xor), but lesser known.  Logically, it is represented as "p => q" or "!p | q".</para>
        /// <para>(More info here: https://en.wikipedia.org/wiki/Truth_table#Logical_implication
        /// and http://whatis.techtarget.com/definition/logical-implication) </para>
        /// </summary>
        /// <param name="torCharacter"></param>
        /// <param name="buffName"></param>
        /// <returns></returns>
        public static bool      HasImpliedMirrorBuff(this TorCharacter torCharacter, string buffName, int stackCount = 1)
        {
            Logger.ContractRequires(() => !string.IsNullOrEmpty(buffName), () => "!string.IsNullOrEmpty(buffName)");

            TorAbility mirrorAbility = torCharacter.GetMirrorAbility(buffName);
            
            return ((mirrorAbility == null) || torCharacter.HasMirrorBuffStacks(buffName, stackCount));
        }

        /// <summary>
        /// <para>Returns true, if the TorCharacter's class or its mirror-class has an ability called ABILITYNAME.</para>
        /// </summary>
        /// <param name="torCharacter"></param>
        /// <param name="abilityName"></param>
        /// <returns></returns>
        public static bool      HasMirrorAbility(this TorCharacter      torCharacter,
                                                 string                 abilityName)
        {
            return (torCharacter.GetMirrorAbility(abilityName) != null);
        }

        /// <summary>
        /// <para>Returns true, if the toon has the BUFFNAME.</para>
        /// Notes:<list type="bullet">
        /// <item><description><para> * The BUFFNAME can either be the toon's class buff name, or the toon's mirror-class buff name.
        /// This method will figure out the correct one to use.</para></description></item>
        /// <item><description><para>Prefer this method to <c>TorCharacter.HasBuff()</c> since this one does class-mirror substitutions.</para></description></item>
        /// </list>
        /// </summary>
        /// <param name="torCharacter">must not be null.</param>
        /// <param name="buffName">if null, empty, or misspelled simply results in a 'false' returned</param>
        /// <returns>'true', if the toon has a minimum of the requested stacks of the identified buff.
        /// Otherwise, returns 'false'.</returns>
        /// 30Jun2012-18:25UTC chinajade
        public static bool      HasMirrorBuff(this TorCharacter             torCharacter,
                                              string                        buffName)
        {
            // Extension methods guarantee the 'this' argument is never null, so no need to check a contract here
            Logger.ContractRequires(() => !string.IsNullOrEmpty(buffName), () => "!string.IsNullOrEmpty(buffName)");

            return (torCharacter.HasBuff(Mirror.LookupBuffName(buffName)));
        }

        /// <summary>
        /// <para>Returns true, iff:<list type="bullet">
        /// <item><para><description> * MINSTACKCOUNT or more stacks of BUFFNAME are found.</description></para></item>
        /// <item><para><description> * The buff meets any requirements imposed by EFFECTQUALIFIER.</description></para></item>
        /// </list></para>
        /// Notes:<list type="bullet">
        /// <item><description><para> * The BUFFNAME can either be the toon's class buff name, or the toon's mirror-class buff name.
        /// This method will figure out the correct one to use.</para></description></item>
        /// <item><description><para> * If MINSTACKCOUNT is omitted, 1 will be used.</para></description></item>
        /// <item><description><para> * If EFFECTQUALIFIER is omitted, no additional requirements will be imposed when searching for the buff.</para></description></item>
        /// <item><description><para>Prefer this method to <c>TorCharacter.HasBuff()</c> since this one does class-mirror substitutions.</para></description></item>
        /// </list>
        /// </summary>
        /// <param name="torCharacter">must not be null.</param>
        /// <param name="debuffName">if null, empty, or misspelled simply results in a 'false' returned</param>
        /// <param name="minStackCount">if omitted, a value of one will be used.</param>
        /// <param name="effectQualifier">if omitted, no additional requirements will be observed when searching for the debuff.</param>
        /// <returns>'true', if the toon has a minimum of the requested stacks of the identified buff.
        /// Otherwise, returns 'false'.</returns>
        /// 29Jun2012-14:08UTC chinajade
        public static bool      HasMirrorBuffStacks(this TorCharacter             torCharacter,
                                                    string                        buffName,
                                                    int                           minStackCount   = 1,
                                                    TorEffectPredicateDelegate    effectQualifier = null)
        {
            // Extension methods guarantee the 'this' argument is never null, so no need to check a contract here
            Logger.ContractRequires(() => !string.IsNullOrEmpty(buffName), () => "!string.IsNullOrEmpty(buffName)");
            Logger.ContractRequires(() => minStackCount > 0, () => string.Format("minStackCount > 0 (minStackCount: {0})", minStackCount));

            effectQualifier = effectQualifier ?? (d => true);   // Resolve null effectQualifier to something sane

            string      normalizedBuffName = Mirror.LookupBuffName(buffName);

            // NB:We don't include the .Stacks check in with the rest of the LINQ query...
            // Although it _should_ be short-circuited (&&), apparently it is not.  And, the SWtOR client gets fussy and
            // throws exceptions if you ask about 'stacks' on a buff that doesn't exist.
            TorEffect buff = torCharacter.Buffs.FirstOrDefault(b => (b.Name == normalizedBuffName) && effectQualifier(b));
            return ((buff != null) && (buff.Stacks >= minStackCount));
        }

        /// <summary>
        /// <para>Returns true, if the toon has the DEBUFFNAME.</para>
        /// Notes:<list type="bullet">
        /// <item><description><para> * The DEBUFFNAME can either be the toon's class debuff name, or the toon's mirror-class debuff name.
        /// This method will figure out the correct one to use.</para></description></item>
        /// <item><description><para>Prefer this method to <c>TorCharacter.HasDebuff()</c> since this one does class-mirror substitutions.</para></description></item>
        /// </list>
        /// </summary>
        /// <param name="torCharacter">must not be null.</param>
        /// <param name="debuffName">if null, empty, or misspelled simply results in a 'false' returned</param>
        /// <returns>'true', if the toon has a minimum of the requested stacks of the identified buff.
        /// Otherwise, returns 'false'.</returns>
        /// 30Jun2012-18:24UTC chinajade
        public static bool HasMirrorDebuff(this TorCharacter    torCharacter,
                                           string               debuffName)
        {
            // Extension methods guarantee the 'this' argument is never null, so no need to check a contract here
            Logger.ContractRequires(() => !string.IsNullOrEmpty(debuffName), () => "!string.IsNullOrEmpty(debuffName)");

            return torCharacter.HasDebuff(Mirror.LookupDebuffName(debuffName));
        }

        /// <summary>
        /// <para>Returns true, iff:<list type="bullet">
        /// <item><para><description> * MINSTACKCOUNT or more stacks of DEBUFFNAME are found.</description></para></item>
        /// <item><para><description> * The debuff meets any requirements imposed by EFFECTQUALIFIER.</description></para></item>
        /// <item><para><description> * The toon is the caster of (owns) the DEBUFFNAME, or the DEBUFFNAME has shared ownership.</description></para></item>
        /// </list></para>
        /// Notes:<list type="bullet">
        /// <item><description><para> * The DEBUFFNAME can either be the toon's class debuff name, or the toon's mirror-class debuff name.
        /// This method will figure out the correct one to use.</para></description></item>
        /// <item><description><para> * If MINSTACKCOUNT is omitted, 1 will be used.</para></description></item>
        /// <item><description><para> * If EFFECTQUALIFIER is omitted, no additional requirements will be imposed when searching for the debuff.</para></description></item>
        /// <item><description><para>Prefer this method to <c>TorCharacter.HasDebuff()</c> since this one does 'ownership' checks and class-mirror substitutions.</para></description></item>
        /// </list>
        /// </summary>
        /// <param name="torCharacter">must not be null.</param>
        /// <param name="debuffName">if null, empty, or misspelled simply results in a 'false' returned</param>
        /// <param name="minStackCount">if omitted, a value of one will be used.</param>
        /// <param name="effectQualifier">if omitted, no additional requirements will be observed when searching for the debuff.</param>
        /// <returns>'true', if the toon has a minimum of the requested stacks of the identified buff.
        /// Otherwise, returns 'false'.</returns>
        /// 29Jun2012-14:08UTC chinajade
        public static bool HasMirrorDebuffStacks(this TorCharacter                torCharacter,
                                                      string                      debuffName,
                                                      int                         minStackCount   = 1,
                                                      TorEffectPredicateDelegate  effectQualifier = null)
        {
            // Extension methods guarantee the 'this' argument is never null, so no need to check a contract here
            Logger.ContractRequires(() => !string.IsNullOrEmpty(debuffName), () => "!string.IsNullOrEmpty(debuffName)");
            Logger.ContractRequires(() => minStackCount > 0, () => string.Format("minStackCount > 0 (minStackCount: {0})", minStackCount));


            effectQualifier = effectQualifier ?? (d => true);   // Resolve null effectQualifier to something sane

            string normalizedDebuffName = Mirror.LookupDebuffName(debuffName);

            return (torCharacter.Debuffs.FirstOrDefault(d => ((d.Name == normalizedDebuffName)
                                                        && effectQualifier(d)
                                                        && ((d.Caster.Name == BuddyTor.Me.Name) || string.IsNullOrEmpty(d.Caster.Name))    // ownership or shared
                                                        && (d.Stacks >= minStackCount)))  != null);
        }

        /// <summary>
        /// <para>Checks if the given Ability is ready to use on the given Target.</para>
        /// <para>Notes:<list type="bullet">
        /// <item><description><para> * If the toon doesn't have the ability, false is returned.</para></description></item>
        /// </list></para>
        /// </summary>
        /// Neo93
        public static bool IsMirrorAbilityReady(this TorPlayer torPlayer, String abilityName, TorCharacter target)
        {
            // Extension methods guarantee the 'this' argument is never null, so no need to check a contract here
            Logger.ContractRequires(() => !string.IsNullOrEmpty(abilityName), () => "!string.IsNullOrEmpty(abilityName)");
            Logger.ContractRequires(() => target != null, () => "target != null");

            TorAbility ability = torPlayer.GetMirrorAbility(abilityName);
            if (ability == null)
                { return (false); }

            EffectResult    effectResult = torPlayer.IsAbilityReady(ability, target);
            //Logger.Write("EFFECT RESULT: " + ability.Name + " - " + effectResult.ToString());
            return (effectResult != EffectResult.NotReady);
        }

        /// <summary>
        /// <para>Returns the advanced class for TorPlayer, if one exists.  If the toon has yet to acquire
        /// an advanced class, the basic class is returned.</para>
        /// <para>Notes:<list type="bullet">
        /// <item><description><para> * If an advanced class is returned, it is returned as a <c>CharacterClass</c> enumeration
        /// value--not an <c>AdvancedClass</c> enumeration.  This is what is meant by 'normalizing'.</para></description></item>
        /// </list>
        /// </para>
        /// </summary>
        /// <returns></returns>
        /// 21Jun2012-20:47UTC chinajade
        public static CharacterClass NormalizedClass(this TorPlayer torPlayer)
        {
            if (torPlayer.AdvancedClass == AdvancedClass.None)
                { return (torPlayer.Class); }
            
            return (MirrorData.AdvancedToCharacterClass[torPlayer.AdvancedClass]);
        }
    }
}
